
(function (context){
    
    rCSV = ("function" == typeof rCSV)?rCSV:require('../rcsv.js');
    jsCSVTester = ("function" == typeof jsCSVTester)?jsCSVTester:require('../javascript-csv-tester.js');

    var tests = [];

    function structureTests(test){
        test.equal (typeof rCSV, "function", "rCSV is a function");
        test.equal (typeof rCSV.parse, "function", "rCSV.parse is a function");
        test.equal (typeof rCSV.toArrays, "function", "rCSV.toArrays is a function");
        test.equal (typeof rCSV.stateNames, "object", "rCSV.close is a function");
        test.deepEqual (rCSV.stateNames, 
                        "ST CM NE ES ET EC ER EF EQ EE CR LF FN XX".split(" "),
                        "rCSV.stateNames: ST CM NE ES ET EC ER EF EQ EE CR LF FN XX");
        var csvp = new rCSV();
        test.equal (typeof csvp, "object", "csvp is an object");
        test.equal (typeof csvp.write, "function", "csvp has 'write' method");
        test.equal (typeof csvp.writeAll, "function", "csvp has 'writeAll' method");
        test.equal (typeof csvp.close, "function", "csvp has 'close' method");
    }
    tests.push(structureTests); //comment this line to remove from the test

    function fsmTests(test){
        var result = null;
        
        var onState = function(dateState,state,pos){
            result.state.push(rCSV.stateNames[state]);
            result.pos.push(pos);
        }
        
        var tests = [
            {   name: "empty buffer; ST:E", input:"",
                expected:{ 
                    state:['ST', 'FN'],
                    pos:  [ 0  , 0]
                }
            },
            {   name: "single comma; ST:C", input:",",
                expected:{ 
                    state:['ST', 'CM', 'FN'],
                    pos:  [ 0,    0,    1]
                }
            },
            {   name: "text; ST:T", input:"a",
                expected:{ 
                    state:['ST', 'NE', 'FN'],
                    pos:  [ 0,    0,    1]
                }
            },
            {   name: "text; ST:T", input:"abc",
                expected:{ 
                    state:['ST', 'NE', 'FN'],
                    pos:  [ 0,    0,    3]
                }
            },
            {   name: "text; ST:R", input:"\r",
                expected:{ 
                    state:['ST', 'CR', 'XX'],
                    pos:  [ 0,    0,    1]
                }
            },
            {   name: "text; ST:R", input:"\r\n",
                expected:{ 
                    state:['ST', 'CR', 'LF', 'FN'],
                    pos:  [ 0,    0,    1,    2]
                }
            },
            {   name: "text; CM:C,T,R", input:",ab,,\r\n",
                expected:{ 
                    state:['ST', 'CM', 'NE', 'CM', 'CM', 'CR', 'LF', 'FN'],
                    pos:  [ 0,    0,    1,    3,    4,    5,    6,    7]
                }
            },
            {   name: "text; ES:C", input:"\",",
                expected:{ 
                    state:['ST', 'ES', 'XX'],
                    pos:  [ 0,    0,    2]
                }
            },
            {   name: "text; ES:T", input:"\"ab",
                expected:{ 
                    state:['ST', 'ES', 'XX'],
                    pos:  [ 0,    0,    3]
                }
            },
            {   name: "text; ES:Q", input:"\"\"a",
                expected:{ 
                    state:['ST', 'ES', 'XX'],
                    pos:  [ 0,    0,    2]
                }
            },
            {   name: "text; ES:Q, EQ:C, CM:Q, EQ:Q, EE:C", input:"\"\",\"\"\"\"",
                expected:{ 
                    state:['ST', 'ES', 'CM', 'ES', 'FN'],
                    pos:  [ 0,    0,    2,    3,    7]
                }
            },
            {   name: "text; multi-line", input:"a,b,c\r\n\"\",d,e",
                expected:{ 
                    state:[ "ST", "NE", "CM", "NE", "CM", "NE", "CR", "LF", "ES", "CM", "NE", "CM", "NE", "FN"],
                    pos:  [0, 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13]
                }
            }
        ];

        for(var i = 0; i <  tests.length; ++i){
            result = {state:[],pos:[]};
            rCSV.parse(tests[i].input, onState);
            test.deepEqual(result, tests[i].expected, tests[i].name)
        }

    }
    tests.push(fsmTests); //comment this line to remove from the test

    function bufferTests(test){
        var result = null;
        
        var tests = [
            {   name: "empty buffer; ST:E", input:"",
                expected:{
                             "buffer": "",
                             "data": [],
                             "pos": 0,
                             "prev": 0,
                             "prevPos": 0,
                             "prevState": 0,
                             "record": [],
                             "state": 0
                         }
            },
            {   name: "text; multi-line", input:"a,b,c\r\n\"\",\"d\"\"\",e",
                expected:{
                             "buffer": ",e",
                             "data": [ [ "a", "b", "c" ] ],
                             "pos": 2,
                             "prev": 2,
                             "prevPos": 1,
                             "prevState": 2,
                             "record": [ "", "d\"" ],
                             "state": 2
                         }
            },
            {   name: "error; multi-line", input:"a,\"\"\"",
                expected:{
                             "buffer": "\"\"\"",
                             "data": [],
                             "pos": 3,
                             "prev": 9,
                             "prevPos": 0,
                             "prevState": 3,
                             "record": [ "a" ],
                             "state": 9
                         }
            }
        ];

        for(var i = 0; i <  tests.length; ++i){
            var ds = rCSV.init();
            if(0 == tests[i].input.length){
                    rCSV.parseBuffer(ds,"",rCSV.onToken);
            }else{
                for(var p = 0; p < tests[i].input.length; ++p){
                    rCSV.parseBuffer(ds,tests[i].input.slice(p,p+1),rCSV.onToken);
                }
            }
            test.deepEqual(ds, tests[i].expected, tests[i].name)
        }
    }
    tests.push(bufferTests); //comment this line to remove from the test

    function toArraysTests(test){
        var result = null;
        
        var onComplete = function(data){
            result = data;
        }
        
        var tests = [
            {   name: "empty buffer; ST:E", input:"",
                expected:[[""]] 
            },
            {   name: "text; multi-line", input:"a,b,c\r\n\"\",d,e",
                expected: [
                    ["a","b","c"],
                    ["","d","e"]
                ]
            }
        ];

        for(var i = 0; i <  tests.length; ++i){
            rCSV.toArrays(tests[i].input, onComplete);
            test.deepEqual(result, tests[i].expected, tests[i].name)
        }

    }
    tests.push(toArraysTests); //comment this line to remove from the test

    function genTests(test){
        var testCSV = (jsCSVTester.genCSVWithMetric({rows:1000,cols:20,max:50,esc:0.5}));
        var expected = testCSV.data;
        var result = rCSV.toArrays(testCSV.text);
        test.deepEqual(result, expected, "Generated CSV");
    }
    tests.push(genTests); //comment this line to remove from the test

    function genBufferTests(test){

        var testCSV = jsCSVTester.genCSVWithMetric({rows:1000,cols:10,max:10,esc:0.5});
        var expected = testCSV.data;
        var ds = rCSV.init();
        for(var p = 0; p < testCSV.text.length; ++p){
            rCSV.parseBuffer(ds,testCSV.text.slice(p,p+1),rCSV.onToken);
        }
        rCSV.closeBuffer(ds, rCSV.onToken);

        test.deepEqual(ds.data, expected, "Generated CSV; Buffered test");
    }
    tests.push(genBufferTests); //comment this line to remove from the test

    function genOOTests(test){

        var testCSV = jsCSVTester.genCSVWithMetric({rows:1000,cols:10,max:10,esc:0.5});
        var expected = testCSV.data;
        var rcsv = new rCSV();
        for(var p = 0; p < testCSV.text.length; ++p){
            rcsv.write(testCSV.text.slice(p,p+1));
        }
        var result = rcsv.close();

        test.deepEqual(result, expected, "Generated CSV; OO Buffered test");
    }
    tests.push(genOOTests); //comment this line to remove from the test

    function fsmSpeedTests(test){
        var result = null;
       
        function cmpa(a,b){
            if(a.length!=b.length){return "different length a:"+a.length+", b:"+b.length;}
            for(var r = 0; r < a.length; ++r){
                if(a[r].length!=b[r].length){
                    return "different length a["+r+"]:"+a[r].length+", b["+r+"]:"+b[r].length;
                }
                for(var c=0; c < a[r].length; ++c){
                    if(a[r][c]!=b[r][c]){
                        return "different value a["+r+"]["+c+"]:"+a[r][c]+", b["+r+"]["+c+"]:"+b[r][c];
                    }
                }
            }
            return "ok";
        } 
        var testCSV = jsCSVTester.genCSVWithMetric({rows:100,cols:20,max:50,esc:0.5});
        
        var expected = testCSV.data;
        var result = rCSV.toArrays(testCSV.text);

        test.deepEqual(result, expected, "speed test correctness");
        test.equal(cmpa(result, expected), "ok", "speed test correctness");
    }
    tests.push(fsmSpeedTests); //comment this line to remove from the test

    function testRunner(test){
        for(var i=0; i < tests.length; ++i){
            tests[i](test);
        }
    }

    context.testRCSV = function(test){
        testRunner(test);
        if(test.done){test.done();}
    };
})(('object' == typeof exports)?module.exports:this);
